package org.yanfengying.car.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.ObjectUtils;
import org.yanfengying.car.domain.CarType;
import org.yanfengying.car.mapper.CarTypeMapper;
import org.yanfengying.car.service.ICarTypeService;
import org.yanfengying.constants.BaseConstants;
import org.yanfengying.utils.service.impl.BastServerImpl;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * <p>
 *  服务实现类
 * </p>
 * 缓存经典问题
 * 缓存穿透:数据库和缓存均没有数据,短时间内大量请求直接访问数据库,数据库压力骤增
 *  解决方法:  1 对于异常请求进行处理
 *           2 对于没有的数据进行一个空值或默认值存储
 *           3 布隆过滤器->避免请求直接访问数据库
 *  缓存击穿:缓存没有数据(某个热点数据过期),数据库有数据,大量的请求直接访问数据库
 *    解决方案: 1 互斥锁->保证同一时间内只有一个业务更新缓存->阻断后续请求直接访问数据库
 *            2  不给热点数据设置过期时间(具体情况具体分析)
 *  缓存雪崩:缓存大量数据同时过期,数据库高并发或者redis宕机
 *    解决方案:1 双key策略
 *           2 在设置过期时间的时候错峰设置(具体情况具体分析)
 */
@Service
public class CarTypeServiceImpl extends BastServerImpl<CarType> implements ICarTypeService {

    @Autowired
    private CarTypeMapper carTypeMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 类型树查询->双可以策略
     * @param pid
     * @return
     */
    @Override
    public List<CarType> getTree(Long pid) {
        //1 先从缓存中获取
        List<CarType> carTypeList = getDates(BaseConstants.CAR_TYPE);
        //2 判断缓存中是否有数据,没有数据就去数据库查询所有并将数据赋值给缓存,有数据就直接返回
        if(Objects.nonNull(carTypeList)){
            return carTypeList;
        }
        //解决缓存击穿(某个热点过期->缓存中没有该数据->大量的请求直接查询数据库)->互斥锁
        synchronized (CarTypeServiceImpl.class){
            //1 先从缓存中获取
            carTypeList = getDates(BaseConstants.CAR_TYPE);
            //2 判断缓存中是否有数据,没有数据就去数据库查询所有并将数据赋值给缓存,有数据就直接返回
            if(Objects.nonNull(carTypeList)){
                return carTypeList;
            }
            //从数据库获取数据并赋值给缓存
            List<CarType> carTypes = getCarTypesLong(pid);
            if(carTypes == null){
                carTypes = new ArrayList<>();
            }
            setDatas(BaseConstants.CAR_TYPE,carTypes);
            return carTypes;
        }

    }

    /**
     * 获取缓存数据
     * @param key
     * @return
     */
    public List<CarType> getDates(String key){
        Object o = redisTemplate.opsForValue().get(key);
        if(Objects.nonNull(o)){//如果 o 不为空->就返回数据
            return (List<CarType>)o;
        }
        //如果主key为空->查询副key
        Object o1 = redisTemplate.opsForValue().get(key + "_bak");
        if(Objects.nonNull(o1)){
            //根据具体需求确定是否将副key的值赋给主key
            return (List<CarType>)o1;
        }
        return null;
    }
    public void setDatas(String key,List<CarType> carTypes){
        redisTemplate.opsForValue().set(key, carTypes);//主key
        redisTemplate.opsForValue().set(key+"_bak", carTypes);//副key
    }
    //方案二->循环
    public List<CarType> getCarTypesLong(Long pid) {
        //方法一:查询所有->循环拿到pid=0的值放第一层->其他的根据pid放到响应的类型中
        List<CarType> carTypeList = new ArrayList<>();
        List<CarType> carTypes = carTypeMapper.loadAll();//拿到所有
        Map<Long, CarType> map = new HashMap<>();
        for (CarType m: carTypes ) {
            map.put(m.getId(), m);
        }
        for (CarType c: carTypes ) {
            if(c.getPid().longValue() == pid.longValue()){
                carTypeList.add(c);//放第一级的类型
            }else {
                CarType carType = map.get(c.getPid());//查父级
                /*List<CarType> list = new ArrayList<>();//赋值错误->每次循环都会重新new空数组
                carType.setChildren(list);*/
                carType.getChildren().add(c);
            }
        }
        return carTypeList;
    }
    /*//方案二->循环 时间复杂度=n*n
    public List<CarType> getCarTypesLong(Long pid) {
        //方法一:查询所有->循环拿到pid=0的值放第一层->其他的根据pid放到响应的类型中
        List<CarType> carTypeList = new ArrayList<>();
        List<CarType> carTypes = carTypeMapper.loadAll();
        for (CarType c: carTypes ) {
            if(c.getPid().longValue() == pid.longValue()){
                carTypeList.add(c);//放第一级的类型
                carTypeList.forEach(System.out::println);
            }else {
                //根据pid将类型放到相应的父类型下面
                for (CarType child: carTypes ) {
                    CarType p = null;
                    if(c.getPid().longValue() == child.getId().longValue()){
                        child.getChildren().add(c);
                        break;
                    }
                }
            }
        }
        return carTypeList;
    }
*/
    //方案一->递归
    public List<CarType> getCarTypesTree(Long pid) {
        List<CarType> childrens = carTypeMapper.tree(pid);
        if(childrens == null){
            return null;
        }
        for (CarType c:childrens  ) {
            c.setChildren(getTree(c.getId()));
        }
        return childrens;
    }
}
